package com.fucking.great.context.engine;

import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import com.fucking.great.context.exception.TxnRuntimeException;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyObject;
import org.codehaus.groovy.control.CompilerConfiguration;

import java.util.Map;

/**
 * java调用groovy工具类（engine方法为执行指定文件） <br/>
 * 每次都会生成新生成class
 * @date 2020-09-14
 */
public class GroovyClassLoaderEngine extends BaseEngine {
    public static final String CLASS_PACKAGE = "com.fucking.great.context.handler.imp";

    protected static final GroovyClassLoader GROOVY_CLASS_LOADER;

    private static final String REPLACEMENT = "_";
    private static final String METHOD_NAME = "service";
    private static final String METHOD_ARG_NAME = "map";
    public static final String OBJECT_NAME = "Object";


    static{
        CompilerConfiguration config = new CompilerConfiguration();
        config.setSourceEncoding("UTF-8");
        // 设置该GroovyClassLoader的父ClassLoader为当前线程的加载器(默认)
        GROOVY_CLASS_LOADER = new GroovyClassLoader(
                Thread.currentThread().getContextClassLoader(), config);
        //
    }
    @Override
    public Object engine(String cacheKey, String scriptStr, Map<String, Object> variable, String... importStr) {
        StringBuilder sb = new StringBuilder();
        // 添加包名:
        sb.append("package " + CLASS_PACKAGE + ";");
        String className = StrUtil.trim(cacheKey);
        if (StrUtil.length(className) > 100) {
            className = cacheKey.substring(0,100);
        }
        // 处理特殊字符:
        className = className.replaceAll("/", REPLACEMENT);


        // 导入所有的包:
        sb.append("import java.util.Map;"); // 添加Map入参的包名.
        if (ArrayUtil.isNotEmpty(importStr)) {
            for (String str : importStr) {
                // import com.alibaba.fastjson.JSONObject;
                if(StrUtil.isNotBlank(str)) // 不为空才导包。
                    sb.append("import ").append(str).append(';');
            }
        }
        // 添加类名.
        sb.append("class ").append(className).append(System.currentTimeMillis()).append('{');
        // 添加方法名
        sb.append("public Object ").append(METHOD_NAME)
                .append("(Map<String, Object> ").append(METHOD_ARG_NAME).append("){");
        /*
            生成局部变量
            String db = (String) variable.get("db");
         */
        for (Map.Entry<String, Object> arg : variable.entrySet()) {
            String argName = arg.getKey();
            Object argValue = arg.getValue();
            if(argValue != null){
                String classSimpleName = getClassSimpleName(argValue.getClass(),importStr);
                // begin
                sb.append(classSimpleName);
                sb.append(' ');
                sb.append(argName);
                sb.append(" = (");
                sb.append(classSimpleName);
                sb.append(')');
                sb.append(METHOD_ARG_NAME);
                sb.append(".get(\"");
                sb.append(argName);
                sb.append("\");");
            }
        }

        // 如果没有return则添加
        if(!StrUtil.contains(scriptStr,"return")){
            sb.append("return ");
        }
        //
        sb.append(scriptStr);
        //
        sb.append('}');
        sb.append('}');
        return engine(cacheKey,sb.toString(),variable);
    }

    @Override
    public Object engine(String cacheKey, String scriptStr, Map<String, Object> variable) {
        Object result = null;
        GroovyObject groovyObject = getGroovyObject(cacheKey,scriptStr);
        result = execMethod(variable, groovyObject);
        return result;
    }

    public Object execMethod(Map<String, Object> variable, GroovyObject groovyObject) {
        Object result;
        result = groovyObject.invokeMethod(METHOD_NAME,variable);
        return result;
    }

    /**
     * 获得短的类名.
     * 解决 bug如果匿名内部类的话,会获得为 "" 。
     * 所以取他上级，递归。
     * @param clas
     * @return
     */
    public static String getClassSimpleName(Class<?> clas,String [] importStr) {
        String simpleName = clas.getSimpleName();
        if(clas == null){ // 接口的getSuperclass() == null , 或者故意传入null
            return null;
        }else if(Object.class.equals(clas)){ // 到了Object类,不往上找了。
            return OBJECT_NAME;
        }else if (StrUtil.EMPTY.equals(simpleName)) { // 解决 bug如果匿名内部类的话,会获得为 ""
            return getClassSimpleName(clas.getSuperclass(),importStr);
        }else if( !ArrayUtil.contains(importStr,clas.getName()) ){ // 解决子类，不在导包里面引起的报错。
            // 先找父类
            String classSimpleName = getClassSimpleName(clas.getSuperclass(), importStr);
            if(OBJECT_NAME.equals(classSimpleName)){
                Class<?>[] interfaces = clas.getInterfaces();
                for (Class<?> anInterface : interfaces) {
                    classSimpleName = getClassSimpleName(anInterface, importStr);
                    // 接口的getSuperclass(),
                    if( StrUtil.isNotBlank(classSimpleName)) break;
                }
            }
            return classSimpleName;
            //
        }else{
            return simpleName;
        }
    }

    public GroovyObject getGroovyObject(String cacheKey,String scriptStr) {
        Class<?> groovyClass = GROOVY_CLASS_LOADER.parseClass(scriptStr);
        GroovyObject groovyObject = null;
        // 获得GroovyShell_2的实例
        try {
            groovyObject = (GroovyObject) groovyClass.newInstance();
        } catch (Exception e) {
            TxnRuntimeException.throwException("GroovyClassLoaderEngine_newInstance",e);
        }
        return  groovyObject;
    }

}
